package com.jzo2o.orders.manager.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.statemachine.OrderStateMachine;
import com.jzo2o.orders.manager.job.OrdersJob;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    @Resource
    private CacheHelper cacheHelper;

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus())
                .select(Orders::getId); //只查询ID字段（目的是为了使用覆盖索引查询，避免回表查询，提升查询效率）
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        List<Orders> records = ordersPage.getRecords();
//        List<Long> ordersIdList = CollUtils.getFieldValues(records, Orders::getId);
        List<Long> orderIdList = records.stream().map(Orders::getId).collect(Collectors.toList());
        if (CollUtils.isEmpty(orderIdList)){
            return CollUtils.emptyList();
        }
        //通过订单Id列表批量查询订单详情
//        List<Orders> ordersList = batchQuery(orderIdList);
//        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(ordersList, OrderSimpleResDTO.class);
//        return orderSimpleResDTOS;
        //1.拼接RedisKey后缀
        String redisKeyPostfix = String.format(RedisConstants.RedisKey.ORDERS, currentUserId);
        //2.调用工具类的方法执行查询（根据没有命中缓存的ID从数据库查询数据，再次追加到缓存中）
        List<OrderSimpleResDTO> orderSimpleResDTOList = cacheHelper.<Long, OrderSimpleResDTO>batchGet(redisKeyPostfix, orderIdList,
                (nocacheIds, clazz) -> {
                 //根据订单ID从数据库查询订单列表
                    List<Orders> ordersList = batchQuery(nocacheIds);
                    if (CollUtils.isEmpty(ordersList)){
                        return new HashMap<>();
                    }
                 //将订单列表转为HashMap（key：订单ID  value：OrderSimpleResDTO对象）
                    Map<Long, OrderSimpleResDTO> orderMap = ordersList.stream().collect(Collectors.toMap(Orders::getId,
                            item -> BeanUtils.copyBean(item, OrderSimpleResDTO.class)));
                    return orderMap;
                },
                OrderSimpleResDTO.class,RedisConstants.Ttl.ORDERS_PAGE_TTL);
        //返回数据
        return orderSimpleResDTOList;

    }
    @Resource
    private OrderStateMachine orderStateMachine;
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
//        Orders orders = queryById(id);
//        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
        String snapshotJSON = orderStateMachine.getCurrentSnapshotCache(id.toString());
        OrderSnapshotDTO orderSnapshotDTO = JsonUtils.toBean(snapshotJSON, OrderSnapshotDTO.class);
        return BeanUtils.copyBean(orderSnapshotDTO, OrderResDTO.class);
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

    /**
     * 取消订单
     *
     * @param dto
     */
    private List<Integer> cancelStatusList = Arrays.asList(OrderStatusEnum.NO_PAY.getStatus(), OrderStatusEnum.DISPATCHING.getStatus());

    @Resource
    private IOrdersManagerService owner;
    @Resource
    private ApplicationContext applicationContext;
    @Override
    public void cancel(OrderCancelReqDTO dto) {
        //1.判断订单是否存在
        Orders orders = this.getById(dto.getId());
        if (orders==null){
            throw new BadRequestException("订单不存在");
        }
        //2.判断订单状态是否合法（必须是未支付或派单中）
        if (!cancelStatusList.contains(orders.getOrdersStatus())){
            throw new BadRequestException("订单状态不合法");
        }
        //3. 如果是未支付 - 取消订单
        if (ObjectUtils.equals(orders.getOrdersStatus(), OrderPayStatusEnum.NO_PAY.getStatus())){
            owner.cancelOrderByNoPay(orders.getId(), dto.getCancelReason(), false);
        }else {
            //4. 如果是待派单 - 关闭订单且退款
            OrdersJob ordersJob = applicationContext.getBean(OrdersJob.class);
            ordersJob.cancelOrderByDispatching(orders,dto.getCancelReason(), UserContext.currentUser());
        }
    }

    @Resource
    private IOrdersCanceledService ordersCanceledService;
    /**
     * 取消未支付订单
     *
     * @param orderId      订单ID
     * @param cancelReason 取消原因
     * @param isSystem     是否系统自动触发 true-系统触发 false-人为触发
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrderByNoPay(Long orderId, String cancelReason, boolean isSystem) {
        //1. 添加取消记录
        OrdersCanceled ordersCanceled = new OrdersCanceled();
        ordersCanceled.getId();//设置订单id
        if (!isSystem){
            ordersCanceled.setCancellerId(UserContext.currentUserId());//取笑人的id
            ordersCanceled.setCancelerName(UserContext.currentUser().getName());//取消人的姓名
            ordersCanceled.setCancellerType(UserContext.currentUser().getUserType());//取消人的类型
        }
        ordersCanceled.setCancelReason(cancelReason);//取消原因
        ordersCanceled.setCancelTime(LocalDateTime.now());//取消时间
        boolean resultForCancel = ordersCanceledService.save(ordersCanceled);
        if (!resultForCancel){
            throw new DBException("添加取消记录失败");
        }
        //2. 更新订单状态 update orders set orders_status=600 where id=?
        boolean resultForOrders = this.lambdaUpdate().eq(Orders::getId, orderId).set(Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus()).update();
        if (!resultForOrders){
            throw new DBException("更新订单状态失败");
        }
    }
}
